package com.teatime.client.net.http;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import com.teatime.client.utils.InputStreamArray;

public class MultiPartInputStream extends InputStream {
	private final int mBoundaryLen = 38;
	private final String mBoundaryPrefix = "------------------teatime";
	private final String mPartHeaderFmt = "--%s\r\nContent-Disposition: form-data; name=\"%s\"";
	private final String mFilePartHeaderFmt = mPartHeaderFmt + "; filename=\"%s\"\r\nContent-Type: %s";
	
	private class Part extends InputStream{
		public long mBodyLength;
		protected InputStreamArray mStreams;

		public Part(InputStream bodyStream, long bodyLength) {
			mBodyLength = bodyLength;
			mStreams = new InputStreamArray();
			mStreams.add(bodyStream);
		}

		public Part(byte[] data) {
			mBodyLength = data.length;
			mStreams = new InputStreamArray();
			mStreams.add(new ByteArrayInputStream(data));
		}
		
		public long length() {
			return mBodyLength;
		}
		
		public int read() throws IOException {
			return mStreams.read();
		}
		
		public int read(byte b[], int off, int len) throws IOException {
			return mStreams.read(b, off, len);
		}
		
		public long skip(long n) throws IOException {
			return mStreams.skip(n);
		}
		
		public int available() throws IOException {
			return mStreams.available();
		}
		
		public void close() throws IOException {
			mStreams.close();
		}
	};
	
	@SuppressWarnings("unused")
	private class NameValuePairPart extends Part{
		public String mName;
		public String mHeader;
		public String mTail;

		public NameValuePairPart(String name, String header, InputStream bodyStream, long bodyLength) {
			super(bodyStream, bodyLength);
			mName = name;
			mHeader = header;
			mTail = "\r\n";
			mStreams.add(0, new ByteArrayInputStream(mHeader.getBytes()));
			mStreams.add(mStreams.size(), new ByteArrayInputStream(mTail.getBytes()));
		}
		
		public NameValuePairPart(String name, InputStream bodyStream, long bodyLength) {
			this(name, 
				getPartHeader(name), 
				bodyStream, 
				bodyLength);
		}
		
		public NameValuePairPart(String name, byte[] value) {
			this(name, 
				new ByteArrayInputStream(value), 
				value.length);
		}
		
		public NameValuePairPart(String name, String value) {
			this(name, 
				new ByteArrayInputStream(value.getBytes()), 
				value.getBytes().length);
		}
		
		public NameValuePairPart(String name, FormFile file) {
			this(name, 
				getFilePartHeader(name, file), 
				file.getInputStream(), 
				file.getFile().length());
		}
		
		public long length() {
			return mHeader.length() + mBodyLength + mTail.length();
		}
		
	};
	
	private String mBoundary;
	private String mEndline;
	private InputStreamArray mParts;
	
	public MultiPartInputStream() {
		mParts = new InputStreamArray();
		generateBoundary();
		mParts.add(new Part(mEndline.getBytes()));
	}
	
	private void generateBoundary() {
		
		mBoundary = mBoundaryPrefix;
		for (int i = 0; i < mBoundaryLen - mBoundaryPrefix.length(); i ++) {
			mBoundary += (int)(Math.random() * 10);
		}
		mEndline = "--" + mBoundary + "--\r\n";
	}
	
	public String getFilePartHeader(String name, FormFile file) {
		return String.format(mFilePartHeaderFmt, 
				mBoundary, 
				name, 
				file.getFileName(), 
				file.getContentType())
				+ "\r\n\r\n";
	}
	
	public String getPartHeader(String name) {
		return String.format(mPartHeaderFmt, 
				mBoundary, 
				name) 
				+ "\r\n\r\n";
	}
	
	private void addPart(Part part) {
		mParts.add(mParts.size() > 0 ? mParts.size() - 1 : 0, part);
	}
	
	public void addPart(String name, FormFile file) {
		Part part = new NameValuePairPart(name, file);
		addPart(part);
	}
	
	public void addPart(String name, String value) {
		Part part = new NameValuePairPart(name, value);
		addPart(part);
	}

	public void addPart(String name, byte[] data) {
		Part part = new NameValuePairPart(name, data);
		addPart(part);
	}
	
	public long length() {
		long length = 0;
		int i;
		for (i = 0; i < mParts.size(); i ++) {
			Part part = (Part)mParts.get(i);
			length += part.length();
		}
		return length;
	}
	
	@Override
	public int read() throws IOException {
		// TODO Auto-generated method stub
		return mParts.read();
	}
	
	@Override
	public int read(byte b[], int off, int len) throws IOException {
		return mParts.read(b, off, len);
	}
	
	public int available() throws IOException {
		return mParts.available();
	}
	
	public void close() throws IOException {
		mParts.close();
	}
	
	public String getBoundary() {
		return mBoundary;
	}
}
